﻿using HPSocketCS;
using log4net;
using McsUtility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MinerManager.Common
{
	public class AgentComponent
	{
		/// <summary>
		/// 本地IP
		/// </summary>
		public string LocalIp { get; set; }

		/// <summary>
		/// 服务器Ip
		/// </summary>
		public string ServerIp { get; set; }

		/// <summary>
		/// 服务器端口
		/// </summary>
		public ushort ServerPort { get; set; }

		ILog logger = LogManager.GetLogger(typeof(Program));

		public event EventHandler<MsgEventArgs> OnMessage;
		public event EventHandler<RcvEventArgs> OnReceive;
		public event EventHandler<EventArgs> OnClose;
		public event EventHandler<EventArgs> OnExceptionClose;
		public event EventHandler<EventArgs> OnShutdown;

		public event EventHandler<ByteEventArgs> OnGetRegSmsCodeReceive;
		public event EventHandler<ByteEventArgs> OnNodeServerMinerManagerGetStaffs;
		public event EventHandler<ByteEventArgs> OnManagerRegisterReceive;
		public event EventHandler<ByteEventArgs> OnGetCurrencysReceive;
		//public event EventHandler<ByteEventArgs> OnManagerGetRedisDataReceive;

		public event EventHandler<ByteEventArgs> OnAddPoolsReceive;
		public event EventHandler<ByteEventArgs> OnEditPoolReceive;
		public event EventHandler<ByteEventArgs> OnDeletePoolReceive;

		public event EventHandler<ByteEventArgs> OnAddWalletReceive;
		public event EventHandler<ByteEventArgs> OnEditWalletReceive;
		public event EventHandler<ByteEventArgs> OnDeleteWalletReceive;

		public event EventHandler<ByteEventArgs> OnGetRePassSmsCodeReceive;
		public event EventHandler<ByteEventArgs> OnRePassReceive;
		public event EventHandler<ByteEventArgs> OnNodeServerMinerManagerGetLogs;
		public event EventHandler<ByteEventArgs> OnNodeServerMinerManagerReStaff;

		public bool IsStarted { get { return agent.IsStarted && connIdForAgent != IntPtr.Zero; } }

		//private Task ObserverTask;
		//private CancellationTokenSource ObserverTokenSource;

		protected TcpPackAgent agent = new TcpPackAgent();

		//Agent唯一的一个连接
		private IntPtr connIdForAgent = IntPtr.Zero;

		/// <summary>
		/// 是否强制关闭（是否调用Stop()关闭）
		/// </summary>
		private bool ForceClose = false;

		/// <summary>
		/// 是否异常关闭
		/// </summary>
		public bool ExceptionClose = false;

		#region AgentComponent()

		public AgentComponent()
		{
			// 设置Client事件         
			agent.MaxPackSize = 0x1000;
			agent.PackHeaderFlag = 0xff;
			agent.OnPrepareConnect += new TcpAgentEvent.OnPrepareConnectEventHandler(OnAgentPrepareConnect);
			agent.OnConnect += new TcpAgentEvent.OnConnectEventHandler(OnAgentConnect);
			agent.OnSend += new TcpAgentEvent.OnSendEventHandler(OnAgentSend);
			agent.OnReceive += new TcpAgentEvent.OnReceiveEventHandler(OnAgentReceive);
			agent.OnClose += new TcpAgentEvent.OnCloseEventHandler(OnAgentClose);
			agent.OnShutdown += new TcpAgentEvent.OnShutdownEventHandler(OnAgentShutdown);
		}

		#endregion

		#region bool Start()

		public bool Start()
		{
			if (string.IsNullOrEmpty(LocalIp)
				|| string.IsNullOrEmpty(ServerIp)
				|| ServerPort == 0)
			{
				throw new Exception("请先设置属性[LocalIp,ServerIp,ServerPort]");
			}

			var success = agent.Start(LocalIp, false);
			if (success == false)
			{
				AddMsg(string.Format(" > Agent start fail -> {0}({1})", agent.ErrorMessage, agent.ErrorCode));
			}

			AgentConnServer();

			success = connIdForAgent != IntPtr.Zero;
			if (!success) { Stop(); };
			
			return success;
		}

		#endregion
		
		#region bool Stop()

		public bool Stop()
		{
			this.ForceClose = true;
			return agent.Stop();
		}

		#endregion
		
		#region bool AgentSend(byte[] package)

		public bool AgentSend(byte[] package)
		{
			if (!IsStarted) return false;
			return agent.Send(connIdForAgent, package, package.Length); 
		}

		#endregion

		#region string ErrorMessage()

		public string ErrorMessage()
		{
			return agent.ErrorMessage;
		}

		#endregion

		#region SocketError ErrorCode()

		public SocketError ErrorCode()
		{
			return agent.ErrorCode;
		}

		#endregion


		#region private void AddMsg(string msg)

		private void AddMsg(string msg)
		{
			OnMessage?.Invoke(this, new MsgEventArgs(msg));
		}

		#endregion

		#region void AgentConnServer()

		public void AgentConnServer()
		{
			IntPtr connId = IntPtr.Zero;
			if (agent.Connect(ServerIp, ServerPort, ref connId) == false)
			{
				if (agent.Connect(ServerIp, ServerPort, ref connId) == false)
				{
					if (agent.Connect(ServerIp, ServerPort, ref connId) == false)
					{
						connIdForAgent = IntPtr.Zero;
						return;
					}
				}
			}
			connIdForAgent = connId;
			this.ForceClose = false;
			this.ExceptionClose = false;
		}

		#endregion

		#region private void Receive(byte[] bytes)

		private void Receive(byte[] bytes)
		{
			OnReceive?.Invoke(this, new RcvEventArgs(bytes));
		}

		#endregion

		#region private void Shutdown()

		private void Shutdown()
		{
			OnShutdown?.Invoke(this, new EventArgs());
		}

		#endregion

		#region Agent事件

		#region protected virtual HandleResult OnAgentPrepareConnect(IntPtr connId, IntPtr socket)

		protected virtual HandleResult OnAgentPrepareConnect(IntPtr connId, IntPtr socket)
		{
			return HandleResult.Ok;
		}

		#endregion

		#region protected virtual HandleResult OnAgentConnect(IntPtr connId)

		protected virtual HandleResult OnAgentConnect(IntPtr connId)
		{
			AddMsg(string.Format(" > [{0},OnAgentConnect]", connId));
			return HandleResult.Ok;
		}

		#endregion

		#region protected virtual HandleResult OnAgentSend(IntPtr connId, byte[] bytes)

		protected virtual HandleResult OnAgentSend(IntPtr connId, byte[] bytes)
		{
			return HandleResult.Ok;
		}

		#endregion

		#region protected virtual HandleResult OnAgentReceive(IntPtr connId, byte[] bytes)

		protected virtual HandleResult OnAgentReceive(IntPtr connId, byte[] bytes)
		{
			var proto = ProtocolHelper.GetProtoType(bytes, body: out byte[] body);
			//string json = System.Text.Encoding.UTF8.GetString(body);
			//logger.Info(proto.ToString() + Environment.NewLine + "    " + json);
			//AddMsg(json);

			switch (proto)
			{
				case ProtoType.NodeServerMinerManagerGetStaffs:
					OnNodeServerMinerManagerGetStaffs?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerManagerGetCurrencysInfo:
					OnGetCurrencysReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				//case ProtoType.NodeServerMinerManagerGetRedisData:
				//	OnManagerGetRedisData?.Invoke(this, new ByteEventArgs(body));
				//	break;
				case ProtoType.NodeServerManagerAddWallets:
					OnAddWalletReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerManagerModifyWallets:
					OnEditWalletReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerManagerDelWallets:
					OnDeleteWalletReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerManagerAddOrePools:
					OnAddPoolsReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerManagerModifyOrePools:
					OnEditPoolReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerManagerDelOrePools:
					OnDeletePoolReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerManagerGetRegSmsCode:
					OnGetRegSmsCodeReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerManagerReg:
					OnManagerRegisterReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerManagerGetRePassSmsCode:
					OnGetRePassSmsCodeReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerManagerRePass:
					OnRePassReceive?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerMinerManagerGetLogs:
					OnNodeServerMinerManagerGetLogs?.Invoke(this, new ByteEventArgs(body));
					break;
				case ProtoType.NodeServerMinerManagerReStaff:
					OnNodeServerMinerManagerReStaff?.Invoke(this, new ByteEventArgs(body));
					break;
				default:
					Receive(bytes);
					break;
			}
			
			return HandleResult.Ok;
		}

		#endregion

		#region protected virtual HandleResult OnAgentClose(IntPtr connId, SocketOperation enOperation, int errorCode)

		protected virtual HandleResult OnAgentClose(IntPtr connId, SocketOperation enOperation, int errorCode)
		{
			if (!ForceClose)
			{
				AgentConnServer();

				this.ExceptionClose = true;
				OnExceptionClose?.Invoke(this, new EventArgs());
			}
			else
			{
				OnClose?.Invoke(this, new EventArgs());

				connIdForAgent = IntPtr.Zero;
				Stop();
			}
			
			//if (!forceClose)//异常掉线，这里做重连操作
			//{
			//	Task.Factory.StartNew(() =>
			//	{
			//		AddMsg(" > [异常掉线，这里做重连操作]");
			//		AgentConnServer();
			//		if (connIdForAgent == IntPtr.Zero)
			//		{
			//			AddMsg(" > [3次连接失败，关闭服务器]");
			//			Stop();
			//		}
			//	});
			//}

			return HandleResult.Ok;
		}

		#endregion

		#region protected virtual HandleResult OnAgentShutdown()

		protected virtual HandleResult OnAgentShutdown()
		{
			Shutdown();
			return HandleResult.Ok;
		}

		#endregion

		#endregion
	}
}
